import { nextId } from './index'
import { EdgeConfig, NodeConfig,Point } from '@logicflow/core';

const domParser = new DOMParser();

/**
 * 生成空的BPMN文档
 */
const getEmptyBpmnXml=():string=>{
  const processId = `Process_${nextId()}`;
  return `<?xml version="1.0" encoding="UTF-8"?>
  <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:flowable="http://flowable.org/bpmn"
    xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
    xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
    xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
    typeLanguage="http://www.w3.org/2001/XMLSchema"
    expressionLanguage="http://www.w3.org/1999/XPath"
    targetNamespace="http://www.flowable.org/processdef"
  >
    <process id="${processId}" isExecutable="true" />
    <bpmndi:BPMNDiagram id="BPMNDiagram_${processId}">
      <bpmndi:BPMNPlane id="BPMNPlane_${processId}" bpmnElement="${processId}" />
    </bpmndi:BPMNDiagram>
  </definitions>`;
}

/**
 * 从BPMN的Document文档对象中查询Process元素
 */
const getProcessElement=(bpmnDoc:Document):Element|null=>{
  return bpmnDoc.querySelector('definitions>process');
}

/**
 * 从BPMN的Document文档对象中查询节点元素
 */
const getNodeElements=(bpmnDoc:Document):Element[]=>{
  const nodeElements:Element[] = [];
  const nodeList = bpmnDoc.querySelectorAll('definitions>process>*:not(sequenceFlow,documentation)');
  nodeList && nodeList.forEach((e)=>{nodeElements.push(e);});
  return nodeElements;
}

/**
 * 从BPMN的Document文档对象中查询节点绘图元素Map
 */
const getShapeElementMap=(bpmnDoc:Document):Map<string,Element>=>{
  const shapeElementMap:Map<string,Element> = new Map();
  const nodeList = bpmnDoc.querySelectorAll("definitions>*>*>BPMNShape");
  nodeList && nodeList.forEach((e)=>{shapeElementMap.set(e.getAttribute('bpmnElement') as string,e);});
  return shapeElementMap;
}

/**
 * 根据节点元素及其绘图元素获取LogicFlow节点配置对象
 */
const getNodeConfig=(nodeElement:Element,shapeElement:Element):NodeConfig=>{
  let x=0,y=0;
  const boundsElement = shapeElement.querySelector('Bounds');
  if(boundsElement){
    x = Number(boundsElement.getAttribute('x'))+Number(boundsElement.getAttribute('width'))/2;
    y = Number(boundsElement.getAttribute('y'))+Number(boundsElement.getAttribute('height'))/2;
  }
  let text = nodeElement.getAttribute('name');
  text = text===null?'':text;
  const nodeConfig:NodeConfig = {
    type: nodeElement.tagName,
    id: nodeElement.id,
    text: text,
    x: x,
    y: y,
    // properties: {}
  };

  // TODO 处理自定义参数

  return nodeConfig;
}

/**
 * 从BPMN的Document文档对象中查询路径元素
 */
const getFlowElements=(bpmnDoc:Document):Element[]=>{
  const flowElements:Element[] = [];
  const nodeList = bpmnDoc.querySelectorAll('definitions>process>sequenceFlow');
  nodeList && nodeList.forEach((e)=>{flowElements.push(e);})
  return flowElements;
}

/**
 * 从BPMN的Document文档对象中查询路径绘图元素Map
 */
const getEdgeElementMap=(bpmnDoc:Document):Map<string,Element>=>{
  const edgeElementMap:Map<string,Element> = new Map();
  const nodeList = bpmnDoc.querySelectorAll("definitions>*>*>BPMNEdge");
  nodeList && nodeList.forEach((e)=>{edgeElementMap.set(e.getAttribute('bpmnElement') as string,e);})
  return edgeElementMap;
}

/**
 * 根据路径元素及其绘图元素获取LogicFlow路径配置对象
 */
const getEdgeConfig=(nodeElement:Element,shapeElement:Element):EdgeConfig=>{
  const waypointElements = shapeElement.querySelectorAll('waypoint');
  const pointsList:Point[] = [];
  waypointElements.forEach((waypointElement)=>{
    pointsList.push({
      x: Number(waypointElement.getAttribute('x')),
      y: Number(waypointElement.getAttribute('y')),
    });
  });

  const edgeConfig:EdgeConfig = {
    type: nodeElement.tagName,
    id: nodeElement.id,
    text: nodeElement.getAttribute('name') as string,
    sourceNodeId: nodeElement.getAttribute('sourceRef') as string,
    targetNodeId: nodeElement.getAttribute('targetRef') as string,
    pointsList,
    // properties: {}
  };

  // TODO 处理自定义参数
  
  return edgeConfig;
}

const convertXml2GraphData = (bpmnXml:any)=>{
  bpmnXml = bpmnXml&&bpmnXml.length>0?bpmnXml:getEmptyBpmnXml();
  // console.log('convertGraph bpmnXml',bpmnXml);
  const graphData = {
    props: { process:{} } as any,
    nodes: [] as NodeConfig[],
    edges: [] as EdgeConfig[]
  }

  const bpmnDoc = domParser.parseFromString(bpmnXml,'text/xml');

  const processElement = getProcessElement(bpmnDoc) as Element;
  graphData.props.process.id = processElement.id;
  graphData.props.process.name = processElement.getAttribute('name');
  graphData.props.process.isExecutable = processElement.getAttribute('isExecutable');
  graphData.props.process.candidateStarterUsers = processElement.getAttribute('flowable:candidateStarterUsers');
  graphData.props.process.candidateStarterGroups = processElement.getAttribute('flowable:candidateStarterGroups');
  graphData.props.process.documentation = processElement.querySelector('documentation')?.innerHTML;
  // TODO 处理其他流程参数

  const nodeElements = getNodeElements(bpmnDoc);
  const shapeElementMap = getShapeElementMap(bpmnDoc);
  nodeElements.forEach((nodeElement)=>{
    graphData.nodes.push(getNodeConfig(nodeElement,shapeElementMap.get(nodeElement.id) as Element));
  });

  const flowElements = getFlowElements(bpmnDoc);
  const edgeElementMap = getEdgeElementMap(bpmnDoc);
  flowElements.forEach((flowElement)=>{
    graphData.edges.push(getEdgeConfig(flowElement,edgeElementMap.get(flowElement.id) as Element));
  });

  console.log('convertGraph graphData',graphData);
  return graphData;
}
export default convertXml2GraphData;